<template>   
    <div class="bg__suspension" ref="baseBox" :style="`z-index: ${zIndex};`">
        <div :class="myBoxClass" @mousedown="mousStart"
            @touchstart="moveStart"
            @touchmove="theMove"
            @touchend="moveEnd"
            :style="`left: ${dLeft}px; top: ${dTop}px; position: ${absolute ? 'absolute' : 'fixed'};`"
            ref="box"
            >
            <div class="no-select" v-if="mode === 'all' || mode === 'suspend'"><slot>测试文本</slot></div>
            <div :class="dialogClass" v-if="mode === 'all' || mode === 'dialog'">
                <div @mousedown.stop @touchstart.stop @touchmove.stop @touchend.stop>
                    <slot name="dialog">对话框测试文本</slot>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
export default {
    name: 'ScSuSpension',
    props: {
        stickup: {
            type: Boolean,
            default: true
        },
        defaultSite: {
            type: Object,
            default() {
                return {
                    x: '',
                    y: ''
                }
            }
        },
        zIndex: {
            type: String,
            default: '9999'
        },
        mode: {
            type: String,
            default: 'suspend'
        },
        dialogClass: {
            type: String,
            default: 'dialog-box'
        },
        boxClass: {
            type: String,
            default: ''
        },
        absolute: {
            type: Boolean,
            default: false
        },
        move: {
            type: Object,
            default() {
                return {
                    x: true,
                    y: true
                }
            }
        }
    },
    data() {
        return {
            dLeft: 0,
            dTop: 0,
            isMove: false,
            dWidth: null,
            dHeight: null,
            eX: null,
            eY: null,
            firstTime: null,
            lastTime: null,
            phone: true,
            marginLeft: 0,
            oldLeft: 0,
            oldTop: 0,
            baseDom: null
        }
    },
    computed: {
        allowXMove() {
            return 'x' in this.$props.move ? this.$props.move.x : true
        },
        allowYMove() {
            return 'y' in this.$props.move ? this.$props.move.y : true
        },
        myBoxClass() {
            return `${this.$props.boxClass} my-box`
        },
        defaultSiteX() {
            return 'x' in this.$props.defaultSite ? this.$props.defaultSite.x : ''
        },
        defaultSiteY() {
            return 'y' in this.$props.defaultSite ? this.$props.defaultSite.y : ''
        }
    },
    mounted() {
        this.init()
        window.onresize = this.init
    },
    methods: {
        init() {
            this.phone = "ontouchstart" in window
            // 解决在 body 元素内容外松开鼠标再回来，元素任然可以移动的 bug
            document.addEventListener('mouseup', () => {
                if(this.phone) return
                this.moveEnd()
            })
            // 解决当鼠标移速过快时，检测不到移动事件的 bug
            document.addEventListener('mousemove', e => {
                this.theMove(e)
            })
            // console.log(this.$refs.box.left);
            
            // 对移动范围进行限制
            this.baseDom = this.absolute ? this.$refs.baseBox : document.querySelector('html')

            // 获取元素的宽高
            this.dWidth = this.$refs.box.offsetWidth
            this.dHeight = this.$refs.box.offsetHeight

            this.dLeft = this.defaultSiteX === '' ? (this.getStyle(this.baseDom, 'width') - this.dWidth) : this.siteInit(this.defaultSiteX, 'x')
            this.dTop = this.defaultSiteY === '' ? this.absolute ? 0 : 400 : this.siteInit(this.defaultSiteY, 'y')
            //  this.getStyle(document.body, 'height') - dHeight - 350

            
        },
        // 位置初始化
        siteInit(site, flag) {
            if(site[0] !== '-')
                return this.handleUnit(site)
            
            const val = this.handleUnit(site.replace('-', ''))
            const w = this.getStyle(document.querySelector('html'), 'width')
            const h = this.getStyle(document.querySelector('html'), 'height')
            
            return flag === 'x' ? w - val - this.dWidth : h - val - this.dHeight

        },
        mousStart(e) { 
            if(this.phone) return
            this.moveStart(e)
        },
        moveStart(event) {
            this.firstTime = new Date()
            // 对 pc 和 手机的适配
            let e = !event.touches ? event : event.touches[0]
            // 对元素的移动状态进行控制
            this.isMove = true

            let father = this.$refs.box.getBoundingClientRect()

            // 会少算 1px 边框
            // let eX = e.offsetX
            // let eY = e.offsetY

            // 设置该元素的位置等
            this.eX = e.clientX - father.left
            this.eY = e.clientY - father.top
            this.dLeft = this.getStyle(this.$refs.box, 'left')
            this.dTop = this.getStyle(this.$refs.box, 'top')
            this.oldLeft = this.getStyle(this.$refs.box, 'left')
            this.oldTop = this.getStyle(this.$refs.box, 'top')
            this.$emit('moveStart')
        },
        theMove(e) {
            if (!this.isMove) return

            e = !e.touches ? e : e.touches[0]

            let father = this.$refs.baseBox.getBoundingClientRect()
            let nLeft = this.absolute ? e.clientX - father.left - this.eX : e.clientX - this.eX
            let nTop = this.absolute ? e.clientY - father.top - this.eY : e.clientY - this.eY

            // 触边碰撞处理
            if (nLeft < 0) {
                nLeft = 0
            } else if (
                nLeft >
                (this.getStyle(this.baseDom, 'width') - this.dWidth)
            ) {
                nLeft = this.getStyle(this.baseDom, 'width') - this.dWidth 
            }

            if (nTop < 0) {
                nTop = 0
            } else if (
                nTop >
                this.getStyle(this.baseDom, 'height') - this.dHeight
            ) {
                nTop = this.getStyle(this.baseDom, 'height') - this.dHeight
            }

            if(this.allowXMove)
                this.dLeft = nLeft
            if(this.allowYMove)
                this.dTop = nTop
        },
        moveEnd() {
            this.isMove = false
            let time = new Date() - this.firstTime
            // 间隔过短，便认定其为点击事件，并复原位置
            if(time < 130) {
                this.dLeft = this.oldLeft
                this.dTop = this.oldTop
                return this.$emit('click')
            }

            if(!this.stickup) return this.$emit('moveEnd')
            
            if ((this.dLeft + this.eX ) >= (this.getStyle(this.baseDom, 'width') / 2)) {

                this.dLeft = this.getStyle(this.baseDom, 'width') - this.dWidth
                this.$emit('moveEnd', 'right')
            } else {
                this.dLeft = 0
                this.$emit('moveEnd', 'left')
            }
        },
        // 获取元素的样式
        getStyle(dom, stl) {
            return parseInt(getComputedStyle(dom)[stl])
        },
        // 处理单位
        handleUnit(value) {
            let _target = '';

            if(this.isNumber(value))
                return value
            const _arr = value.match(/^([0-9]+)(.+)/)
            if(!_arr) return value

            const [_all, val, unit] = _arr

            switch (unit) {
                case 'px':
                    _target = val
                    break;
                case 'rem':
                    {
                        const htmlFontSize = this.getStyle(document.querySelector('html'), 'fontSize');
                        _target = val * htmlFontSize
                    }
                    break;
                case 'vw':
                    {
                        const w = this.getStyle(document.querySelector('html'), 'width');
                        _target = val * 0.01 * w
                    }
                    break;
                case 'vh':
                    {
                        const h = this.getStyle(document.querySelector('html'), 'height');
                        _target = val * 0.01 * h
                    }
                    break;
                default:
                    _target = val
                    break;
            }

            return _target
        },
        isNumber(str) {
            return /^[0-9]*$/.test(str)
        }
    }
}
</script>

<style scoped>
.bg__suspension {
    touch-action: none;
    position: relative;
}
.bg__suspension .my-box {
    cursor: pointer;
    overflow: hidden;
    -webkit-user-drag: none;
}
.no-select {
    user-select: none;
}
.bg__suspension .dialog-box {
    padding: 15px 30px;
    background: rgb(101, 215, 230);
    display: flex;
    align-items: center;
    justify-content: center;
}
</style>